package cn.slipi.core.plugin.post;

import cn.slipi.common.constants.Constants;
import cn.slipi.common.constants.RedisConstants;
import cn.slipi.common.entity.PdrApi;
import cn.slipi.common.entity.RequestLog;
import cn.slipi.common.enums.PluginDetailEnum;
import cn.slipi.common.rule.custom.entity.MatchCondition;
import cn.slipi.common.utils.cache.redis.RedisClient;
import cn.slipi.core.cache.ApiManager;
import cn.slipi.core.plugin.IPlugin;
import cn.slipi.core.plugin.IPluginChain;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.reactive.function.BodyExtractors;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Objects;

/**
 * ResponsePlugin
 */
public class ResponsePlugin implements IPlugin {
    private static final Logger LOGGER = LoggerFactory.getLogger(ResponsePlugin.class);

    @Autowired
    private ApiManager apiManager;

    @Autowired
    private RedisClient redisClient;

    @Override
    public Mono<Void> execute(ServerWebExchange exchange, IPluginChain chain) {
        RequestLog requestLog = (RequestLog) exchange.getAttributes().get(Constants.REQUEST_LOG);

        MatchCondition requestCondition = (MatchCondition) exchange.getAttributes().get(Constants.GATEWAY_HEADER);
        PdrApi pdrApi = apiManager.getApiByCode(requestCondition.getApiCode());
        return chain.execute(exchange)
                .then(Mono.defer(() -> {
                    ServerHttpResponse response = exchange.getResponse();
//                    response.getHeaders().setContentType(this.matchMediaType(response, pdrApi));
                    ClientResponse clientResponse = exchange.getAttribute(Constants.WEBCLIENT_RESPONSE);
                    if (response.getStatusCode() != HttpStatus.OK) {
                        requestLog.setResponse(response.getStatusCode().toString());
                        exchange.getAttributes().put(Constants.REQUEST_LOG, requestLog);
                        return response.writeWith(
                                Mono.just(exchange.getResponse().bufferFactory().wrap(JSON.toJSONBytes(Objects.requireNonNull(response.getStatusCode()).getReasonPhrase())))
                        );
                    }

                    requestLog.setResponse("ok");
                    requestLog.setEndTimestamp(System.currentTimeMillis());
                    exchange.getAttributes().put(Constants.REQUEST_LOG, requestLog);

                    LOGGER.info(JSONObject.toJSONString(requestLog));
                    this.redisClient.lPush(RedisConstants.REQUEST_LOG, JSONObject.toJSONString(requestLog));

                    return response.writeWith(clientResponse.body(BodyExtractors.toDataBuffers()));
                }));
    }

    private MediaType matchMediaType(ServerHttpResponse response, PdrApi pdrApi) {
        String transType = pdrApi.getTranslateProtocol().toLowerCase();
        switch (transType) {
            case "xml":
                return MediaType.APPLICATION_XML;
            case "json":
                return MediaType.APPLICATION_JSON_UTF8;
            default:
                return response.getHeaders().getContentType();
        }
    }

    @Override
    public String getPluginPhase() {
        return PluginDetailEnum.RESPONSE.getPhase();
    }

    @Override
    public int getCode() {
        return PluginDetailEnum.RESPONSE.getCode();
    }

    @Override
    public String getName() {
        return PluginDetailEnum.RESPONSE.getName();
    }
}
